package cn.tedu.csmall.server.filter;

import cn.tedu.csmall.server.security.LoginPrinciple;
import cn.tedu.csmall.server.util.JwtUtils;
import cn.tedu.csmall.server.web.JsonResult;
import cn.tedu.csmall.server.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

@Component
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        SecurityContextHolder.clearContext();

        //获取jwt
        String jwt = request.getHeader("Authorization");

        if (!StringUtils.hasText(jwt)) {
            log.debug("无jwt数据，放行");
            filterChain.doFilter(request, response);
            return;

        }
        Claims claims = null;

        try {
            claims = JwtUtils.parseJwt(jwt);
        } catch (ExpiredJwtException e) {
            String errMsg = "登录信息已过期";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, errMsg);
            String jsonString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            response.setContentType("application/json;charset=UTF-8");
            writer.println(jsonString);
            return;
        } catch (SignatureException e) {
            String errMsg = "解析jwt失败，签名错误";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_INVALID, errMsg);
            String jsonString = JSON.toJSONString(jsonResult);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(jsonString);
            return;
        } catch (MalformedJwtException e) {
            log.debug("解析JWT失败，JWT数据有误：{},{}", e.getClass().getName(), e.getMessage());
            String errMessage = "获取登录信息失败，请重新登录";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_INVALID, errMessage);
            String jsonString = JSON.toJSONString(jsonResult);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(jsonString);
            return;
        } catch (Throwable e) {
            log.debug("解析JWT失败，错误信息：{},{}", e.getClass().getName(), e.getMessage());
            String errMessage = "获取登录信息失败，请重新登录";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_INVALID, errMessage);
            String jsonString = JSON.toJSONString(jsonResult);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(jsonString);
            return;
        }

        log.debug("解析jwt完毕");
        Object id = claims.get("id");
        Object username = claims.get("username");
        Object authoritiesStr = claims.get("authorities");
        List<SimpleGrantedAuthority> authorities = JSON.parseArray(authoritiesStr.toString(), SimpleGrantedAuthority.class);
        //存入SecurityContext
        LoginPrinciple loginPrinciple = new LoginPrinciple();
        loginPrinciple.setId(Long.parseLong(id.toString()));
        loginPrinciple.setUsername(username.toString());
        Authentication authentication = new UsernamePasswordAuthenticationToken(loginPrinciple, null, authorities);
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authentication);
        filterChain.doFilter(request, response);

    }
}
